#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

// Cuando se reciba la siguiente senial se generara un numero aleatorio
#define SENIAL_DE_ENTRADA SIGUSR1

#define ARCHIVO_FIFO "./fifo"

#define INTERVALO_ESPERA 2 //segundos
#define RAND(MIN,MAX) ( (rand() % ((MAX+1)-(MIN))) + (MIN) )

int pedidos_encolados = 0;
int descriptor_fifo = 0;

void handler (int);
int generar_numero();
void cerrar_descriptores();
void recibe_signal(int signum, siginfo_t *siginfo, void *context);

int main()
{
    // Inicializacion //////////////////////////////////////////////////////////
    
    struct sigaction gestion;
    gestion.sa_sigaction=recibe_signal;
    sigfillset(&gestion.sa_mask);
    gestion.sa_flags = SA_SIGINFO;
    int j;

    for(j=1;j<32;j++)
    {
        if(j!= 9)
            sigaction(j,&gestion,NULL);
    }

    if(( mkfifo(ARCHIVO_FIFO, 0666) ) < 0) //crea un fifo con permisos read y write para todos
    {
        printf("Error en mkfifo.\n"); //perror
        exit(1);
    }    
    
    descriptor_fifo = open(ARCHIVO_FIFO, O_RDWR); //abre el fifo para read y write
    if (descriptor_fifo < 0)
    {
        printf("Error en el archivo FIFO.\n");
        unlink(ARCHIVO_FIFO);
        exit(1);
    }
    
    srand(getpid());

    // Bucle principal /////////////////////////////////////////////////////////
    
    for(;;)
    {
        while(pedidos_encolados == 0)
        {
            // Si no hay pedidos encolados por recibirse la senial, esperamos
            pause();
        }
        
        // Se recibio la senial, generamos un numero y decrementamos la cantidad
        // de pedidos encolados
        if( generar_numero() < 0 )
        {
            // Si hubo un error salimos del bucle principal y terminamos la
            // ejecucion
            break;
        }
        else
        {
            pedidos_encolados--;
        }
        
        // Esperamos INTERVALO_ESPERA segundos, usamos el while por si una
        // senial interrumpe al sleep antes de que se cumpla el tiempo deseado
        int segundos_faltantes = sleep(INTERVALO_ESPERA);
        while(segundos_faltantes > 0)
        {
            segundos_faltantes = sleep(segundos_faltantes);
        }
    }
    // Cerramos los descriptores
    cerrar_descriptores();

    return 0;
}

void recibe_signal (int signum, siginfo_t *siginfo, void *context)
{
    switch(signum)
    {
       
        case 10/*SENIAL_DE_ENTRADA*/:
            pedidos_encolados++;
            //printf("Pedidos encolados: %d\n", pedidos_encolados);
            break;
        case 2/*SIGINT*/:
            cerrar_descriptores();
            exit(0);
            break;
        default:
            cerrar_descriptores();
            exit(0);
            break;

    }
}

int generar_numero()
{
    int num = RAND(0,100);
    
    // Salida por consola
    printf("%d\n", num);
    
    // Salida por FIFO
    if( write(descriptor_fifo, &num, sizeof(num)) < 0 )
        return -1;
    
    return num;
}

void cerrar_descriptores()
{
    close(descriptor_fifo);
    unlink(ARCHIVO_FIFO);
}